package similarity;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;

import preprocess.XMLContracter;
import preprocess.XMLDoc;
import utils.Configuration;
import wvtools.WVToolsWrapper;

public class TextSimilarity implements Similarity
{
    private static String                             wordListFile;
    private static String                             idListFile;
    private HashMap<XMLDoc, HashMap<XMLDoc, Double>>  resultMap;
    private HashMap<XMLDoc, HashMap<Integer, Double>> vsmMap;
    private WVToolsWrapper                            wvtools;

    static
    {
        wordListFile = Configuration.WordListFile;
        idListFile = Configuration.IDListFile;
    }

    private void addResult(XMLDoc doc1, XMLDoc doc2, double result)
    {
        if (!Configuration.cacheXML)
            return;
        if (resultMap == null)
        {
            resultMap = new HashMap<XMLDoc, HashMap<XMLDoc, Double>>();
        }

        if (!resultMap.containsKey(doc1))
        {
            resultMap.put(doc1, new HashMap<XMLDoc, Double>());
        }
        resultMap.get(doc1).put(doc2, result);

        if (!resultMap.containsKey(doc2))
        {
            resultMap.put(doc2, new HashMap<XMLDoc, Double>());
        }
        resultMap.get(doc2).put(doc1, result);
    }

    private double getResult(XMLDoc doc1, XMLDoc doc2)
    {
        if (!Configuration.cacheXML)
            return -1;
        if (resultMap == null) { return -1; }
        if (!resultMap.containsKey(doc1)) { return -1; }
        HashMap<XMLDoc, Double> tmp = resultMap.get(doc1);
        if (!tmp.containsKey(doc2)) { return -1; }
        return tmp.get(doc2);
    }

    public TextSimilarity()
    {
        try
        {
            wvtools = WVToolsWrapper.newInstance();
            vsmMap = new HashMap<XMLDoc, HashMap<Integer, Double>>();
            File wordListPath = new File(wordListFile);
            File idListPath = new File(idListFile);
            if (wordListPath.exists() && idListPath.exists())
            {
                wvtools.load(wordListPath, idListPath);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    @Override
    public double calcSimilarity(XMLDoc doc1, XMLDoc doc2)
    {
        double result = getResult(doc1, doc2);
        if (result != -1) { return result; }
        try
        {
            double[] arr1, arr2;
            HashMap<Integer, Double> map1, map2;

            if (Configuration.cacheXML)
            {
                if (!vsmMap.containsKey(doc1))
                {
                    arr1 = wvtools.createWordVector(doc1, false).getValues();
                    // TODO 内存释放
                    map1 = arrayToHashMap(arr1);
                    vsmMap.put(doc1, map1);
                    // map1 = wordlist.createVSMMap(doc1);
                    // vsmMap.put(doc1, map1);
                }
                if (!vsmMap.containsKey(doc2))
                {
                    arr2 = wvtools.createWordVector(doc2, false).getValues();
                    map2 = arrayToHashMap(arr2);
                    vsmMap.put(doc2, map2);
                    // map2 = wordlist.createVSMMap(doc2);
                    // vsmMap.put(doc2, map2);
                }
                map1 = vsmMap.get(doc1);
                map2 = vsmMap.get(doc2);
            }
            else
            {
                arr1 = wvtools.createWordVector(doc1, false).getValues();
                map1 = arrayToHashMap(arr1);
                arr2 = wvtools.createWordVector(doc2, false).getValues();
                map2 = arrayToHashMap(arr2);
            }
            // WVTWordVector vec1 = wvtools.createWordVector(doc1, false);
            // WVTWordVector vec2 = wvtools.createWordVector(doc2, false);
            // result = vectorSimilarity(vec1.getValues(), vec2.getValues());
            result = vectorSimilarity(map1, map2);
            addResult(doc1, doc2, result);
            return result;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return -1;
        }
    }

    private double vectorSimilarity(HashMap<Integer, Double> map1,
            HashMap<Integer, Double> map2)
    {
        double result = 0;
        for (int i : map1.keySet())
        {
            if (map2.containsKey(i))
            {
                result += map1.get(i) * map2.get(i);
            }
        }
        return result;
    }

    private static HashMap<Integer, Double> arrayToHashMap(double[] array)
    {
        HashMap<Integer, Double> result = new HashMap<Integer, Double>();
        for (int i = 0; i < array.length; i++)
        {
            if (Math.abs(array[i]) > 0.0001)
            {
                result.put(i, array[i]);
            }
        }
        return result;
    }

    @SuppressWarnings("unused")
    private double vectorSimilarity(double[] v1, double[] v2)
    {
        if (v1.length != v2.length) { return -1; }
        int len = v1.length;
        double value = 0;
        for (int i = 0; i < len; i++)
        {
            value += v1[i] * v2[i];
        }
        return value;
    }

    public static void main(String[] args) throws DocumentException,
            IOException
    {
        SAXReader reader = new SAXReader();
        Document d1 = reader.read(new File(
                "D:/INEX Dataset/test set/1474166/44635.xml"));
        Document d2 = reader.read(new File(
                "D:/INEX Dataset/test set/1474166/199298.xml"));
        XMLContracter contracter = new XMLContracter();
        d1 = contracter.contract(d1, XMLContracter.NESTING);
        d2 = contracter.contract(d2, XMLContracter.NESTING);
        // XMLTree.showTree(d1);
        // XMLTree.showTree(d2);
        XMLDoc doc1 = new XMLDoc(d1, "T1");
        XMLDoc doc2 = new XMLDoc(d2, "T2");
        System.out.println(new TextSimilarity().calcSimilarity(doc1, doc2));
        // System.out.println(new TextSimilarity().calcSimilarity(doc1, doc2));
    }
}
